/* cc -O3 -o bokgen bokgen.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef __STDC_VERSION__
#define inline
#else
#if __STDC_VERSION < 199901L
#define inline
#endif
#endif

#ifndef nitems
#define nitems(v) (sizeof(v)/sizeof(*v))
#endif

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

#define U8CLEN(c) \
    (((c) >> 7 & 1) == 0 ? 1 : \
     ((c) >> 6 & 1) == 0 ? 1 : \
     ((c) >> 5 & 1) == 0 ? 2 : \
     ((c) >> 4 & 1) == 0 ? 3 : \
     ((c) >> 3 & 1) == 0 ? 4 : 1)

typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;

#define JP 0
#define EN 1
#define LKJ 2
#define LKE 3

#define ISNAME(l, c) (isnametable[(c)&0xFF]>>(l)&1)
const uchar isnametable[] = {
    /*  "�",  "�",  "�",  "�",  "�",  "�",  "�",  "�",  */
    /*  "�", "あ", "い", "う", "え", "お", "か", "き", */
          0,    5,    5,    5,    5,    5,    5,    5,
    /*  "�",  "�",  "�",  "�",  "�",  "�",  "�",  "�",  */
    /* "く", "け", "こ", "さ", "し", "す", "せ", "そ", */
          5,    5,    5,    5,    5,    5,    5,    5,
    /*  "�",  "�",  "�",  "�",  "�",  "�",  "�",  "�",  */
    /* "よ", "た", "ち", "つ", "て", "と", "な", "に", */
          5,    5,    5,    5,    5,    5,    5,    5,
    /*  "�",  "�",  "�",  "�",  "�",  "�",  "�",  "�",  */
    /* "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ", */
          5,    5,    5,    5,    5,    5,    5,    5,
    /*  " ",  "!", "\"",  "#",  "÷",  "%",  "&",  "'",  */
    /* "下", "ま", "み", "む", "め", "も", "や", "ゆ", */
         10,    5,    5,    5,    5,    5,    5,   15,
    /*  "(",  ")",  "*",  "+",  ",",  "-",  ".",  "/",  */
    /* "よ", "ら", "り", "る", "れ", "ろ", "わ", "を", */
          5,    5,    5,    5,   15,   15,   15,   15,
    /*  "0",  "1",  "2",  "3",  "4",  "5",  "6",  "7",  */
    /* "左", "ん", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "っ", */
         10,   15,   15,   15,   15,   15,   15,   15,
    /*  "8",  "9",  ":",  ";",  "<",  "=",  ">",  "?",  */
    /* "ゃ", "ゅ", "ょ", "が", "ぎ", "ぐ", "げ", "ご", */
         15,   15,    5,    5,    5,    5,    5,    5,
    /*  "@",  "A",  "B",  "C",  "D",  "E",  "F",  "G",  */
    /* "右", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ", */
          0,   15,   15,   15,   15,   15,   15,   15,
    /*  "H",  "I",  "J",  "K",  "L",  "M",  "N",  "O",  */
    /* "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ", */
         15,   15,   15,   15,   15,   15,   15,   15,
    /*  "P",  "Q",  "R",  "S",  "T",  "U",  "V",  "W",  */
    /* "東", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ",  "｡",  "､", */
         10,   15,   15,   15,   15,   15,   10,   10,
    /*  "X",  "Y",  "Z",  "[",  "×",  "]",  "^",  "_",  */
    /* "~",  "ー",  "…",  "�",  "�",  "�",  "�",  "�", */
         15,   15,   10,    0,    0,    0,    0,    0,
    /*  "`",  "a",  "b",  "c",  "d",  "e",  "f",  "g", */
    /* "西", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", */
          0,   15,   15,   15,   15,   15,   15,   15,
    /*  "h",  "i",  "j",  "k",  "l",  "m",  "n",  "o",  */
    /* "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", */
         15,   15,   15,   15,   15,   15,   15,   15,
    /*  "p",  "q",  "r",  "s",  "t",  "u",  "v",  "w",  */
    /* "南", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", */
         10,   15,   15,   15,   15,   15,   15,   15,
    /*  "x",  "y",  "z",  "{",  "|",  "}",  "¯",  "⋅", */
    /* "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", */
         15,   15,   15,    5,    5,    5,    5,    5,
    /* "北", "マ", "ミ", "ム", "メ", "モ", "ラ", "リ", */
          0,    5,    5,    5,    5,    5,    5,    5,
    /* "ル", "レ", "ロ", "ヤ", "ユ", "ヨ", "ワ", "ヲ", */
          5,    5,    5,    5,    5,    5,    5,    5,
    /* "大", "ン", "ァ", "ィ", "ゥ", "ェ", "ォ", "ッ", */
          0,    5,    5,    5,    5,    5,    5,    5,
    /* "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ", */
          5,    5,    5,    5,    5,    5,    5,    5,
    /* "中", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ", */
          0,    5,    5,    5,    5,    5,    5,    5,
    /* "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ", */
          5,    5,    5,    5,    5,    5,    5,    5,
    /* "小", "パ", "ピ", "プ", "ペ", "ポ", "・", "：", */
          0,    5,    5,    5,    5,    5,    5,    0,
    /* "；", "「", "」", "＋",  "×",  "℃",  "℉",  "�", */
          0,    0,    0,    0,    0,    0,    0,    0,
    /*  "�",  "↑",  "↓",  "→",  "←",  "★",  "♥",  "♪", */
          0,    0,    0,    0,    0,    0,    0,    0,
    /* "ヴ",  "Ⅰ",  "Ⅱ",  "Ⅲ",  "�",  "�",  "�",  "�", */
          5,    0,    0,    0,    0,    0,    0,    0,
    /* "風", "白", "黒", "赤", "青", "黄", "緑", "金", */
          0,    0,    0,    0,    0,    0,    0,    0,
    /* "銀", "紫",  "�", "火", "炎", "災", "水", "氷", */
          0,    0,    0,    0,    0,    0,    0,    0,
    /* "永", "太", "陽", "年", "月", "日", "時", "分", */
          0,    0,    0,    0,    0,    0,    0,    0,
    /* "秒", "春", "夏", "秋",  " ", "之", "ヶ", "々", */
          0,    0,    0,    0,    5,    0,    0,    0,
    /* "＝", "丈", "片", "己", "凶", "歯",  "�",  "�", */
          0,    0,    0,    0,    0,    0,    0,    0,
    /*  "�",  "�",  "�",  "�",  "�",  "�",  "�",  "�" */
          0,    0,    0,    0,    0,    0,    0,    0
};

const uchar echtable[] = {
    /* "�", "�", "�", "�", "�", "�", "�", "�", */
    /* "�", "�", "�", "�", "�", "�", "�", "�", */
    /* "�", "�", "�", "�", "�", "�", "�", "�", */
    /* "�", "�", "�", "�", "�", "�", "�", "�", */
    /* " ", "!", "\"", "#", "÷", "%", "&", "'", */
        32,                                39,
    /* "(", ")", "*", "+", ",", "-", ".", "/", */
                            44,  45,  46,  47,
    /* "0", "1", "2", "3", "4", "5", "6", "7", */
        48,  49,  50,  51,  52,  53,  54,  55,
    /* "8", "9", ":", ";", "<", "=", ">", "?", */
        56,  57,
    /* "@", "A", "B", "C", "D", "E", "F", "G", */
             65,  66,  67,  68,  69,  70,  71,
    /* "H", "I", "J", "K", "L", "M", "N", "O", */
        72,  73,  74,  75,  76,  77,  78,  79,
    /* "P", "Q", "R", "S", "T", "U", "V", "W", */
        80,  81,  82,  83,  84,  85,  86,  87,
    /* "X", "Y", "Z", "[", "×", "]", "^", "_", */
        88,  89,  90,
    /* "`", "a", "b", "c", "d", "e", "f", "g", */
             97,  98,  99, 100, 101, 102, 103,
    /* "h", "i", "j", "k", "l", "m", "n", "o", */
       104, 105, 106, 107, 108, 109, 110, 111,
    /* "p", "q", "r", "s", "t", "u", "v", "w", */
       112, 113, 114, 115, 116, 117, 118, 119,
    /* "x", "y", "z", "{", "|", "}", "¯", "⋅", */
       120, 121, 122
};

const uchar jwidthtable[] = {
    /* "�",  "あ", "い", "う", "え", "お", "か", "き", */
         1,    2,    2,    2,    2,    2,    2,    2,
    /* "く", "け", "こ", "さ", "し", "す", "せ", "そ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "よ", "た", "ち", "つ", "て", "と", "な", "に", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "下", "ま", "み", "む", "め", "も", "や", "ゆ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "よ", "ら", "り", "る", "れ", "ろ", "わ", "を", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "左", "ん", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "っ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "ゃ", "ゅ", "ょ", "が", "ぎ", "ぐ", "げ", "ご", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "右", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "東", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ",  "｡",  "､", */
          2,    2,    2,    2,    2,    2,    1,    1,
    /*  "~", "ー",  "…",  "�",  "�",  "�",  "�",  "�", */
          1,    2,    1,    1,    1,    1,    1,    1,
    /* "西", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "南", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "北", "マ", "ミ", "ム", "メ", "モ", "ラ", "リ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "ル", "レ", "ロ", "ヤ", "ユ", "ヨ", "ワ", "ヲ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "大", "ン", "ァ", "ィ", "ゥ", "ェ", "ォ", "ッ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "中", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "小", "パ", "ピ", "プ", "ペ", "ポ", "・", "：", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "；", "「", "」", "＋",  "×",  "℃",  "℉",  "�", */
          2,    2,    2,    2,    1,    1,    1,    1,
    /*  "�",  "↑",  "↓",  "→",  "←",  "★",  "♥",  "♪", */
          1,    1,    1,    1,    1,    1,    1,    1,
    /* "ヴ",  "Ⅰ",  "Ⅱ",  "Ⅲ",  "�",  "�",  "�",  "�", */
          2,    1,    1,    1,    1,    1,    1,    1,
    /* "風", "白", "黒", "赤", "青", "黄", "緑", "金", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "銀", "紫",  "�", "火", "炎", "災", "水", "氷", */
          2,    2,    1,    2,    2,    2,    2,    2,
    /* "永", "太", "陽", "年", "月", "日", "時", "分", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /* "秒", "春", "夏", "秋",  " ", "之", "ヶ", "々", */
          2,    2,    2,    2,    1,    2,    2,    2,
    /* "＝", "丈", "片", "己", "凶", "歯", "�",  "�", */
          2,    2,    2,    2,    2,    2,    2,    2,
    /*  "�",  "�",  "�",  "�",  "�",  "�",  "�",  "�" */
          1,    1,    1,    1,    1,    1,    1,    1,
};

const uchar jchtable[] = {
    /* "�",  "あ", "い", "う", "え", "お", "か", "き", */
                1,    2,    3,    4,    5,    6,    7,
    /* "く", "け", "こ", "さ", "し", "す", "せ", "そ", */
          8,    9,   10,   11,   12,   13,   14,   15,
    /* "よ", "た", "ち", "つ", "て", "と", "な", "に", */
         16,   17,   18,   19,   20,   21,   22,   23,
    /* "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ", */
         24,   25,   26,   27,   28,   29,   30,   31,
    /* "下", "ま", "み", "む", "め", "も", "や", "ゆ", */
               33,   34,   35,   36,   37,   38,   39,
    /* "よ", "ら", "り", "る", "れ", "ろ", "わ", "を", */
         40,   41,   42,   43,   44,   45,   46,   47,
    /* "左", "ん", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "っ", */
               49,   50,   51,   52,   53,   54,   55,
    /* "ゃ", "ゅ", "ょ", "が", "ぎ", "ぐ", "げ", "ご", */
         56,   57,   58,   59,   60,   61,   62,   63,
    /* "右", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ", */
               65,   66,   67,   68,   69,   70,   71,
    /* "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ", */
         72,   73,   74,   75,   76,   77,   78,   79,
    /* "東", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ", "｡",  "､", */
               81,   82,   83,   84,   85,
    /* "~",  "ー", "…",  "�",  "�",  "�",  "�",  "�", */
         88,   89,
    /* "西", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", */
               97,   98,   99,  100,  101,  102,  103,
    /* "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", */
        104,  105,  106,  107,  108,  109,  110,  111,
    /* "南", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", */
              113,  114,  115,  116,  117,  118,  119,
    /* "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", */
        120,  121,  122,  123,  124,  125,  126,  127,
    /* "北", "マ", "ミ", "ム", "メ", "モ", "ラ", "リ", */
              129,  130,  131,  132,  133,  134,  135,
    /* "ル", "レ", "ロ", "ヤ", "ユ", "ヨ", "ワ", "ヲ", */
        136,  137,  138,  139,  140,  141,  142,  143,
    /* "大", "ン", "ァ", "ィ", "ゥ", "ェ", "ォ", "ッ", */
              145,  146,  147,  148,  149,  150,  151,
    /* "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ", */
        152,  153,  154,  155,  156,  157,  158,  159,
    /* "中", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ", */
              161,  162,  163,  164,  165,  166,  167,
    /* "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ", */
        168,  169,  170,  171,  172,  173,  174,  175,
    /* "小", "パ", "ピ", "プ", "ペ", "ポ", "・", "：", */
              177,  178,  179,  180,  181,  182,
    /* "；", "「", "」", "＋", "×",  "℃",  "℉",  "�", */
    /* "�",  "↑",  "↓",  "→",  "←",  "★",  "♥",  "♪", */
    /* "ヴ", "Ⅰ",  "Ⅱ",  "Ⅲ",  "�",  "�",  "�",  "�", */
        200,
    /* "風", "白", "黒", "赤", "青", "黄", "緑", "金", */
    /* "銀", "紫", "�", "火", "炎", "災", "水", "氷", */
    /* "永", "太", "陽", "年", "月", "日", "時", "分", */
    /* "秒", "春", "夏", "秋", " ", "之", "ヶ", "々", */
                                236,
    /* "＝", "丈", "片", "己", "凶", "歯", "�",  "�", */
    /* "�",  "�",  "�",  "�",  "�",  "�",  "�",  "�" */
};

const char *utftable[2][256] = {{
      "", "あ", "い", "う", "え", "お", "か", "き",
    "く", "け", "こ", "さ", "し", "す", "せ", "そ",
    "よ", "た", "ち", "つ", "て", "と", "な", "に",
    "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ",
    "下", "ま", "み", "む", "め", "も", "や", "ゆ",
    "よ", "ら", "り", "る", "れ", "ろ", "わ", "を",
    "左", "ん", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "っ",
    "ゃ", "ゅ", "ょ", "が", "ぎ", "ぐ", "げ", "ご",
    "右", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ",
    "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ",
    "東", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ",  "｡",  "､",
    "~",  "ー",  "…",  "�",  "�",  "�",  "�",  "�",
    "西", "ア", "イ", "ウ", "エ", "オ", "カ", "キ",
    "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ",
    "南", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ",
    "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ",
    "北", "マ", "ミ", "ム", "メ", "モ", "ラ", "リ",
    "ル", "レ", "ロ", "ヤ", "ユ", "ヨ", "ワ", "ヲ",
    "大", "ン", "ァ", "ィ", "ゥ", "ェ", "ォ", "ッ",
    "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ",
    "中", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ",
    "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ",
    "小", "パ", "ピ", "プ", "ペ", "ポ", "・", "：",
    "；", "「", "」", "＋",  "×",  "℃",  "℉",  "�",
     "�",  "↑",  "↓",  "→",  "←",  "★",  "♥",  "♪",
    "ヴ",  "Ⅰ",  "Ⅱ",  "Ⅲ",  "�",  "�",  "�",  "�",
    "風", "白", "黒", "赤", "青", "黄", "緑", "金",
    "銀", "紫",  "�", "火", "炎", "災", "水", "氷",
    "永", "太", "陽", "年", "月", "日", "時", "分",
    "秒", "春", "夏", "秋", " ",  "之", "ヶ", "々",
    "＝", "丈", "片", "己", "凶", "歯",  "�",  "�",
     "�",  "�",  "�",  "�",  "�",  "�",  "�",  "�"
}, {
     "", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    " ", "!", "\"", "#", "÷", "%", "&", "'",
    "(", ")", "*", "+", ",", "-", ".", "/",
    "0", "1", "2", "3", "4", "5", "6", "7",
    "8", "9", ":", ";", "<", "=", ">", "?",
    "@", "A", "B", "C", "D", "E", "F", "G",
    "H", "I", "J", "K", "L", "M", "N", "O",
    "P", "Q", "R", "S", "T", "U", "V", "W",
    "X", "Y", "Z", "[", "×", "]", "^", "_",
    "`", "a", "b", "c", "d", "e", "f", "g",
    "h", "i", "j", "k", "l", "m", "n", "o",
    "p", "q", "r", "s", "t", "u", "v", "w",
    "x", "y", "z", "{", "|", "}", "¯", "⋅",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�",
    "�", "�", "�", "�", "�", "�", "�", "�"
}};

const char * const b64table[2][64] = {{
    /* JP */
    "あ", "い", "う", "え", "お", "か", "き", "く",
    "け", "こ", "さ", "し", "す", "せ", "そ", "た",
    "ち", "つ", "て", "と", "な", "に", "ぬ", "ね",
    "の", "は", "ひ", "ふ", "へ", "ほ", "ま", "み",
    "む", "め", "も", "や", "ゆ", "よ", "ら", "り",
    "る", "れ", "ろ", "わ", "を", "が", "ぎ", "ぐ",
    "げ", "ご", "ざ", "じ", "ず", "ぜ", "ぞ", "だ",
    "ぢ", "づ", "で", "ど", "ば", "び", "ぶ", "べ"
}, {
    /* EN */
     "B",  "C",  "D",  "F",  "G",  "H",  "J",  "K",
     "L",  "M",  "N",  "P",  "Q",  "R",  "S",  "T",
     "V",  "W",  "X",  "Y",  "Z",  "b",  "c",  "d",
     "f",  "g",  "h",  "j",  "k",  "l",  "m",  "n",
     "p",  "q",  "r",  "s",  "t",  "v",  "w",  "x",
     "y",  "z",  "0",  "1",  "2",  "3",  "4",  "5",
     "6",  "7",  "8",  "9",  "?",  "!",  "@",  "#",
     "=",  "^",  ">",  "/",  "-",  "_",  "+",  ":"
}};

const uchar kbdtable[2][66] = {{
    /* JP */
     0,  1,  2,  3,  4,  30, 31, 32, 33, 34,  60, 61,
     5,  6,  7,  8,  9,  35, 36, 37, 43, 44,  62, 63,
    10, 11, 12, 13, 14,  38, 39, 40, 41, 42,  64,
    15, 16, 17, 18, 19,  45, 46, 47, 48, 49,  65,
    20, 21, 22, 23, 24,  50, 51, 52, 53, 54,
    25, 26, 27, 28, 29,  55, 56, 57, 58, 59
}, {
    /* EN */
     0,  1,  2,  3,  4,  5,  6,  42, 43, 44, 45, 46, 47,
     7,  8,  9, 10, 11, 12, 13,  48, 49, 50, 51, 62, 56,
    14, 15, 16, 17, 18, 19, 20,  63, 64, 60, 61, 58, 57,
    21, 22, 23, 24, 25, 26, 27,  55, 59,     65,
    28, 29, 30, 31, 32, 33, 34,  52, 53,
    35, 36, 37, 38, 39, 40, 41,  54,
}};

const uchar kbdrev[2][66] = {{
    /* JP */
     0,  1,  2,  3,  4,  12, 13, 14, 15, 16,
    24, 25, 26, 27, 28,  35, 36, 37, 38, 39,
    46, 47, 48, 49, 50,  56, 57, 58, 59, 60,
     5,  6,  7,  8,  9,  17, 18, 19, 29, 30,
    31, 32, 33, 20, 21,  40, 41, 42, 43, 44,
    51, 52, 53, 54, 55,  61, 62, 63, 64, 65,
    10, 11, 22, 23, 34,  45
}, {
    /* EN */
     0,  1,  2,  3,  4,  5,  6,  13, 14, 15, 16, 17, 18, 19,
    26, 27, 28, 29, 30, 31, 32,  39, 40, 41, 42, 43, 44, 45,
    49, 50, 51, 52, 53, 54, 55,  58, 59, 60, 61, 62, 63, 64,
     7,  8,  9, 10, 11, 12, 20, 21, 22, 23,
    56, 57, 65, 46, 25, 38, 37, 47, 35, 36, 24, 33, 34, 48
}};

#define RIGHT 0
#define UP 1
#define LEFT 2
#define DOWN 3
const uchar kbdmovetable[2][264] = {{
    /* JP */
    /* right */
    1,  2,  3,  4,   5,   6,  7,  8,  9, 10,  11, 0,
    13, 14, 15, 16, 17,  18, 19, 20, 21, 22,  23, 12,
    25, 26, 27, 28, 29,  30, 31, 32, 33, 34,  23,
    36, 37, 38, 39, 40,  41, 42, 43, 44, 45,  35,
    47, 48, 49, 50, 51,  52, 53, 54, 55, 45,
    57, 58, 59, 60, 61,  62, 63, 64, 65, 45,
    /* up */
    56, 57, 58, 59, 60,  61, 62, 63, 64, 65,  45, 45,
    0,  1,  2,  3,  4,    5,  6,  7,  8,  9,  10, 11,
    12, 13, 14, 15, 16,  17, 18, 19, 20, 21,  22,
    24, 25, 26, 27, 28,  29, 30, 31, 32, 33,  34,
    35, 36, 37, 38, 39,  40, 41, 42, 43, 44,
    46, 47, 48, 49, 50,  51, 52, 53, 54, 55,
    /* left */
    11, 0,  1,  2,  3,   4,   5,  6,  7,  8,   9, 10,
    23, 12, 13, 14, 15,  16, 17, 18, 19, 20,  21, 22,
    34, 24, 25, 26, 27,  28, 29, 30, 31, 32,  33,
    45, 35, 36, 37, 38,  39, 40, 41, 42, 43,  44,
    45, 46, 47, 48, 49,  50, 51, 52, 53, 54,
    45, 56, 57, 58, 59,  60, 61, 62, 63, 64,
    /* down */
    12, 13, 14, 15, 16,  17, 18, 19, 20, 21,  22, 23,
    24, 25, 26, 27, 28,  29, 30, 31, 32, 33,  34, 34,
    35, 36, 37, 38, 39,  40, 41, 42, 43, 44,  45,
    46, 47, 48, 49, 50,  51, 52, 53, 54, 55,  10,
    56, 57, 58, 59, 60,  61, 62, 63, 64, 65,
    0,   1,  2,  3,  4,   5,  6,  7,  8,  9
}, {
    /* EN */
    /* right */
     1,  2,  3,  4,  5,  6,  7,   8,  9, 10, 11, 12,  0,
    14, 15, 16, 17, 18, 19, 20,  21, 22, 23, 24, 25, 13,
    27, 28, 29, 30, 31, 32, 33,  34, 35, 36, 37, 38, 26,
    40, 41, 42, 43, 44, 45, 46,  47, 48,     39,
    50, 51, 52, 53, 54, 55, 56,  57, 48,
    59, 60, 61, 62, 63, 64, 65,  57,
    /* up */
    58, 59, 60, 61, 62, 63, 64,  65, 57, 48, 48, 48, 48,
     0,  1,  2,  3,  4,  5,  6,   7,  8,  9, 10, 11, 12,
    13, 14, 15, 16, 17, 18, 19,  20, 21, 22, 23, 24, 25,
    26, 27, 28, 29, 30, 31, 32,  33, 34,     36,
    39, 40, 41, 42, 43, 44, 45,  46, 47,
    49, 50, 51, 52, 53, 54, 55,  56,
    /* left */
    12,  0,  1,  2,  3,  4,  5,   6,  7,  8,  9, 10, 11,
    25, 13, 14, 15, 16, 17, 18,  19, 20, 21, 22, 23, 24,
    38, 26, 27, 28, 29, 30, 31,  32, 33, 34, 35, 36, 37,
    48, 39, 40, 41, 42, 43, 44,  45, 46,     47,
    48, 49, 50, 51, 52, 53, 54,  55, 56,
    48, 58, 59, 60, 61, 62, 63,  64,
    /* down */
    13, 14, 15, 16, 17, 18, 19,  20, 21, 22, 23, 24, 25,
    26, 27, 28, 29, 30, 31, 32,  33, 34, 35, 36, 37, 38,
    39, 40, 41, 42, 43, 44, 45,  46, 47, 48, 48, 48, 48,
    49, 50, 51, 52, 53, 54, 55,  56, 57,     10,
    58, 59, 60, 61, 62, 63, 64,  65, 65,
     0,  1,  2,  3,  4,  5,  6,   7,
}};

#define LKJX(c) (lktable[0][(c)&0x3F])
#define LKJY(c) (lktable[1][(c)&0x3F])
#define LKJA(c) (lktable[2][(c)&0x3F])
const uchar lktable[3][64] = {{
    /* X */
    0, 0, 0, 0, 0, 1, 1, 1,
    1, 1, 2, 2, 2, 2, 2, 3,
    3, 3, 3, 3, 4, 4, 4, 4,
    4, 5, 5, 5, 5, 5, 6, 6,
    6, 6, 6, 7, 7, 7, 8, 8,
    8, 8, 8, 9, 9, 1, 1, 1,
    1, 1, 2, 2, 2, 2, 2, 3,
    3, 3, 3, 3, 5, 5, 5, 5
}, {
    /* Y */
    0, 1, 2, 3, 4, 0, 1, 2,
    3, 4, 0, 1, 2, 3, 4, 0,
    1, 2, 3, 4, 0, 1, 2, 3,
    4, 0, 1, 2, 3, 4, 0, 1,
    2, 3, 4, 0, 1, 2, 0, 1,
    2, 3, 4, 0, 1, 0, 1, 2,
    3, 4, 0, 1, 2, 3, 4, 0,
    1, 2, 3, 4, 0, 1, 2, 3
}, {
    /* Accent */
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1
}};

#define WRAPDIST(a, b, w) (((a) + (w) - (b)) % (w))
#define ABSDIST(a, b, w) MIN(WRAPDIST((a), (b), (w)), WRAPDIST((b), (a), (w)))
#define LJ64DIST(a, b) \
  (ABSDIST(LKJX(a), LKJX(b), 10) + ABSDIST(LKJY(a), LKJY(b), 7) + LKJA(b))

#define BOK1 0
#define BOK2 1
#define BOK3 2
#define BOK4 4

#define BOK1REGION(l)                     0,             3, 0
#define BOK1CHECKSUM(l)                   3,            16, 0
#define BOK1OFFSET(l)                    19,             2, 0
#define BOK1SOL(l)                       21,             9, 0
#define BOK1TIMEZONE(l)                  30,   (6+((l)&1)), 0
#define BOK1HOURS(l)           (36+((l)&1)),             6, 0
#define BOK1MINUTES(l)         (42+((l)&1)),             6, 0
#define BOK1DIFFICULTY(l)      (48+((l)&1)),             3, 0
#define BOK1DUNGEONS(l)        (51+((l)&1)),             5, 0
#define BOK1CLEARS(l)          (56+((l)&1)),             3, 0
#define BOK1CONTINUES(l)       (59+((l)&1)),             4, 0
#define BOK1CAUGHT(l)          (63+((l)&1)),             5, 0
#define BOK1KILLS(l)           (68+((l)&1)),             7, 0
#define BOK1RANK(l)            (75+((l)&1)),             4, 0
#define BOK1TITLE(l)           (79+((l)&1)),             5, 0
#define BOK1NAME(l)            (84+((l)&1)), (5+((l)&1)*4)
#define BOK1LINKBATTLES(l) (124+((l)&1)*33),             6, 0
#define BOK1LINKTRADES(l)  (130+((l)&1)*33),             6, 0
#define BOK1LOAN(l)        (136+((l)&1)*33),             8, 0
#define BOK1PADDING(l)     (144+((l)&1)*33),  (((l)&1)*15), 0

#define BOK2CHECKSUM(l)                 0,            16, 0
#define BOK2HEADERPADDING(l)           16,             2, 0
#define BOK2REGION(l)                  18,             3, 0
#define BOK2OFFSET(l)                  21,             3, 0
#define BOK2TIMEZONE(l)                24,   (6+((l)&1)), 0
#define BOK2SIDE(l)          (30+((l)&1)),             3, 0
#define BOK2STYLE(l)         (33+((l)&1)),             4, 0
#define BOK2KILLS(l)         (37+((l)&1)),             8, 0
#define BOK2FORGES(l)        (45+((l)&1)),             7, 0
#define BOK2LINKBATTLES(l)   (52+((l)&1)),             6, 0
#define BOK2LINKSHOPPING(l)  (58+((l)&1)),             6, 0
#define BOK2SOL(l)           (64+((l)&1)),            10, 1
#define BOK2LOAN(l)          (74+((l)&1)),             8, 1
#define BOK2HOURS(l)         (82+((l)&1)),             6, 0
#define BOK2TITLES(l)        (88+((l)&1)),            12, 0
#define BOK2TITLEPADDING(l) (100+((l)&1)),            16, 0
#define BOK2NAME(l)         (104+((l)&1)), (5+((l)&1)*4)
#define BOK2PADDING(l)   (144+((l)&1)*33),  (((l)&1)*15), 0

#define BOK3CHECKSUM(l)                 0,            16, 0
#define BOK3HEADERPADDING(l)           16,             2, 0
#define BOK3REGION(l)                  18,             3, 0
#define BOK3OFFSET(l)                  21,             3, 0
#define BOK3TIMEZONE(l)                24,   (6+((l)&1)), 0
#define BOK3KILLS(l)         (30+((l)&1)),             8, 0
#define BOK3FORGES(l)        (38+((l)&1)),             7, 0
#define BOK3RACES(l)         (45+((l)&1)),             9, 0
#define BOK3LINKRACES(l)     (54+((l)&1)),             7, 0
#define BOK3CROSSLINKED(l)   (61+((l)&1)),             1, 0
#define BOK3ENDINGS(l)       (62+((l)&1)),             4, 0
#define BOK3ENDINGPADDING(l) (66+((l)&1)),             2, 0
#define BOK3SOL(l)           (68+((l)&1)),            10, 0
#define BOK3HOURS(l)         (86+((l)&1)),             6, 0
#define BOK3LOAN(l)          (78+((l)&1)),             8, 0
#define BOK3TITLES(l)        (92+((l)&1)),            10, 0
#define BOK3TITLEPADDING(l) (102+((l)&1)),             2, 0
#define BOK3NAME(l)         (104+((l)&1)), (5+((l)&1)*4)
#define BOK3PADDING(l)   (144+((l)&1)*33),  (((l)&1)*15), 0

#define BOK4CHECKSUM(l)       0, 16, 0
#define BOK4HEADERPADDING(l) 16,  2, 0
#define BOK4REGION(l)        18,  3, 0
#define BOK4OFFSET(l)        21,  3, 0
#define BOK4TITLES(l)        24, 14, 0
#define BOK4DIFFICULTY(l)    38,  2, 0
#define BOK4HOURS(l)         40,  7, 0
#define BOK4SOL(l)           47, 15, 0
#define BOK4SWORD(l)         62,  3, 0
#define BOK4GUN(l)           65,  3, 0
#define BOK4TERRENNIAL(l)    68,  3, 0
#define BOK4CLIMATE(l)       71,  3, 0
#define BOK4NAMEDARK(l)      74, 10
#define BOK4NAMESOLAR(l)    154, 10
#define BOK4PADDING(l)      234,  6, 0

static int getint(const uchar *data, uint o, uint w, int s) {
    uint i;
    int v;

    for (v = 0, i = 0; i < w; i++, o++)
        v |= (data[o / 6] >> o % 6 & 1) << i;
    if (s && w > 0 && (v >> w & 1))
        v = -((v ^ (1 << w) - 1) + 1 & (1 << w) - 1);
    return v;
}

static void putint(uchar *data, int v, uint o, uint w, int s) {
    uint i;
    int m;

    m = (1 << (w - !!s)) - 1;
    v = v > m ? m : v;
    m = s ? -m+1 : 0;
    v = v < m ? m : v;
    for (i = 0; i < w; i++, o++) {
        data[o/6] &= ~(1 << o % 6);
        data[o/6] |= (v >> i & 1) << o % 6;
    }
}

static inline void getstr(const uchar *data, uchar *str, uint o, uint n) {
    uint i;

    for (i = 0; i < n; i++, o += 8)
        str[i] = getint(data, o, 8, 0);
}

static inline void putstr(uchar *data, const uchar *str, uint o, uint n) {
    uint i;

    for (i = 0; i < n; i++, o += 8)
        putint(data, str[i], o, 8, 0);
}

static inline uint encstr(uchar *dst, uint dn, const char *src, uint sn,
  uint lang) {
    uint i, j, k, l;

    for (i = 0, j = 0, lang &= 1; i < sn && src[i] != '\0'; i += l) {
        l = U8CLEN(src[i]);
        l = MIN(l, sn - i);
        for (k = 0; k < nitems(utftable[0]) &&
          strncmp(src + i, utftable[lang][k], l) != 0; k++) {}
        if (k == nitems(utftable[0]))
            break;
        if (dst != NULL && j < dn)
            dst[j] = k;
        j++;
    }
    for (; j < dn; j++)
        if (dst != NULL)
            dst[j] = 0;
    return (j);
}

static inline uint decstr(char *dst, uint dn, const uchar *src, uint sn,
  uint lang) {
    const char *c;
    uint i, j, k;

    for (i = 0, j = 0, lang &= 1; i < sn && src[i] != 0; i++) {
        c = (uint)src[i] < nitems(utftable[0]) ? utftable[lang][src[i]] : "�";
        for (k = 0; c[k] != '\0'; j++, k++)
            if (j < dn)
                dst[j] = c[k];
    }
    if (j < dn)
        dst[j] = '\0';
    return (j);
}

static inline int valstr(const uchar *str, uint n, uint lang) {
    uint i, j;

    if (lang > 1)
        return 0;
    for (i = 0; i < n && str[i] != '\0'; i++)
        if (!ISNAME(lang, str[i]))
            return 0;
    for (j = i; j < n && str[j] == '\0'; j++) {}
    return i != 0 && j == n;
}

const uchar bokxortable[4][64] = {{
    0x3F, 0x29, 0x16, 0x32, 0x27, 0x05, 0x1E, 0x0E,
    0x1F, 0x31, 0x06, 0x3A, 0x37, 0x0D, 0x1E, 0x36,
    0x3F, 0x39, 0x16, 0x02, 0x07, 0x15, 0x1E, 0x1E,
    0x1F, 0x01, 0x26, 0x0A, 0x37, 0x1D, 0x1E, 0x06,
    0x3F, 0x09, 0x16, 0x12, 0x27, 0x25, 0x1E, 0x2E,
    0x1F, 0x11, 0x06, 0x1A, 0x37, 0x2D, 0x1E, 0x16,
    0x3F, 0x19, 0x16, 0x22, 0x07, 0x35, 0x1E, 0x3E,
    0x1F, 0x21, 0x26, 0x2A, 0x37, 0x3D, 0x1E, 0x26
}, {
    0x3F, 0x08, 0x34, 0x39, 0x03, 0x10, 0x38, 0x31,
    0x37, 0x18, 0x0C, 0x09, 0x1B, 0x10, 0x10, 0x11,
    0x0F, 0x28, 0x04, 0x39, 0x13, 0x10, 0x08, 0x31,
    0x07, 0x18, 0x1C, 0x29, 0x2B, 0x10, 0x20, 0x11,
    0x1F, 0x08, 0x14, 0x39, 0x23, 0x10, 0x18, 0x31,
    0x17, 0x18, 0x2C, 0x09, 0x3B, 0x10, 0x30, 0x11,
    0x2F, 0x28, 0x24, 0x39, 0x33, 0x10, 0x28, 0x31,
    0x27, 0x18, 0x3C, 0x29, 0x0B, 0x10, 0x00, 0x11
}, {
    0x3F, 0x29, 0x16, 0x32, 0x27, 0x05, 0x1E, 0x0E,
    0x1F, 0x31, 0x06, 0x3A, 0x37, 0x0D, 0x1E, 0x36,
    0x3F, 0x39, 0x16, 0x02, 0x07, 0x15, 0x1E, 0x1E,
    0x1F, 0x01, 0x26, 0x0A, 0x37, 0x1D, 0x1E, 0x06,
    0x3F, 0x09, 0x16, 0x12, 0x27, 0x25, 0x1E, 0x2E,
    0x1F, 0x11, 0x06, 0x1A, 0x37, 0x2D, 0x1E, 0x16,
    0x3F, 0x19, 0x16, 0x22, 0x07, 0x35, 0x1E, 0x3E,
    0x1F, 0x21, 0x26, 0x2A, 0x37, 0x3D, 0x1E, 0x26
}, {
    0x3F, 0x1D, 0x06, 0x16, 0x07, 0x29, 0x1E, 0x22,
    0x2F, 0x15, 0x06, 0x2E, 0x27, 0x21, 0x0E, 0x1A,
    0x1F, 0x0D, 0x06, 0x06, 0x07, 0x19, 0x3E, 0x12,
    0x2F, 0x05, 0x06, 0x1E, 0x27, 0x11, 0x0E, 0x0A,
    0x3F, 0x3D, 0x06, 0x36, 0x07, 0x09, 0x1E, 0x02,
    0x2F, 0x35, 0x06, 0x0E, 0x27, 0x01, 0x0E, 0x3A,
    0x1F, 0x2D, 0x06, 0x26, 0x07, 0x39, 0x3E, 0x32,
    0x2F, 0x25, 0x06, 0x3E, 0x27, 0x31, 0x0E, 0x2A
}};

#define BOKXOR(p, g, o, o2, n) \
do { \
    uint _i = (o); \
    for (; _i < (n); _i++) \
        ((uchar *)(p))[_i] ^= bokxortable[g][_i+(o2)]; \
} while (0)

#define BOK1CONST 0x19
#define BOK2CONST 0x2E
#define BOK3CONST 0x19
#define BOK4CONST 0x15
static void bokxor(uchar *dst, const uchar *src, uint o, uint n, uint x) {
    uint i, t;

    for (i = 0, t = 0x3F; i < o; i++)
        t ^= (x = 0x1D * x + 1 & 0x3F);
    for (i = 0; i < n; i++) {
        dst[i] = src[i] ^ (i >= 4 || n < 10 ? t : 0);
        t ^= (x = 0x1D * x + 1 & 0x3F);
    }
}

static void mapstep(uint *map, uint d, uint i, const uchar *move, uint count,
  const uchar *mod) {
    uint c;

    if (i >= count)
        return;
    c = mod != NULL ? mod[i] : i;
    if (map[c] < d)
        return;
    map[c] = d;
    mapstep(map, d+1, move[RIGHT * count + i], move, count, mod);
    mapstep(map, d+1, move[UP * count + i], move, count, mod);
    mapstep(map, d+1, move[LEFT * count + i], move, count, mod);
    mapstep(map, d+1, move[DOWN * count + i], move, count, mod);
}

static uint *mapkbd(uint lang) {
    const uint c = nitems(kbdtable[0]);
    uint *map;
    uint i, z = c * c * sizeof(*map);

    if (lang > 2 || (map = malloc(z)) == NULL)
        return NULL;
    if (lang < 2) {
        (void)memset(map, 0x55, z);
        for (i = 0; i < c; i++)
            mapstep(map + c * i, 0, kbdrev[lang][i], kbdmovetable[lang], c,
              kbdtable[lang]);
    } else {
        for (i = 0; i < c * c; i++)
            map[i / c * c + i % c] = i % c > 63 ? 99 : LJ64DIST(i / c, i % c);
    }
    return map;
}

static void printmap(uint *map, uint c, const uchar *remap) {
    uint i, j;

#define NEXT \
    (i++, map[remap != NULL ? (i-1) - (i-1) % c + remap[(i-1)%c] : i-1])
    for (i = 0; i < c * c;) {
        (void)printf("%2u:\n", i / c);
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 2; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar('\n');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 2; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar('\n');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 1; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar('\n');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 1; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar('\n');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar('\n');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar(' ');
        for (j = 0; j < 5; j++)
            (void)printf(" %2u", NEXT);
        (void)putchar('\n');
    }
#undef NEXT
}

ushort bokcrctable[4][256] = {{
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
    0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
    0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
    0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
    0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
    0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
    0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
    0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
    0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
    0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
    0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
    0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
    0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
    0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
    0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
    0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
    0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
    0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
    0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
    0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
    0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
    0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
    0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
    0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
    0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
    0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
    0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
    0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
    0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
    0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
    0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
    0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
}, {
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
    0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
    0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
    0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
    0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
    0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
    0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
    0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
    0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
    0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
    0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
    0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
    0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
    0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
    0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
    0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
    0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
    0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
    0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
    0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
    0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
    0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
    0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
    0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
    0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
    0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
    0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
    0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
    0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
    0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
    0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
    0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
}, {
    0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
    0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
    0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
    0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
    0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
    0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
    0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
    0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
    0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
    0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
    0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
    0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
    0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
    0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
    0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
    0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
    0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
    0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
    0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
    0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
    0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
    0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
    0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
    0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
    0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
    0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
    0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
    0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
    0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
    0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
    0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
    0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202,
}, {
    0x0000, 0x180D, 0x301A, 0x2817, 0x6034, 0x7839, 0x502E, 0x4823,
    0xC068, 0xD865, 0xF072, 0xE87F, 0xA05C, 0xB851, 0x9046, 0x884B,
    0x98DD, 0x80D0, 0xA8C7, 0xB0CA, 0xF8E9, 0xE0E4, 0xC8F3, 0xD0FE,
    0x58B5, 0x40B8, 0x68AF, 0x70A2, 0x3881, 0x208C, 0x089B, 0x1096,
    0x29B7, 0x31BA, 0x19AD, 0x01A0, 0x4983, 0x518E, 0x7999, 0x6194,
    0xE9DF, 0xF1D2, 0xD9C5, 0xC1C8, 0x89EB, 0x91E6, 0xB9F1, 0xA1FC,
    0xB16A, 0xA967, 0x8170, 0x997D, 0xD15E, 0xC953, 0xE144, 0xF949,
    0x7102, 0x690F, 0x4118, 0x5915, 0x1136, 0x093B, 0x212C, 0x3921,
    0x536E, 0x4B63, 0x6374, 0x7B79, 0x335A, 0x2B57, 0x0340, 0x1B4D,
    0x9306, 0x8B0B, 0xA31C, 0xBB11, 0xF332, 0xEB3F, 0xC328, 0xDB25,
    0xCBB3, 0xD3BE, 0xFBA9, 0xE3A4, 0xAB87, 0xB38A, 0x9B9D, 0x8390,
    0x0BDB, 0x13D6, 0x3BC1, 0x23CC, 0x6BEF, 0x73E2, 0x5BF5, 0x43F8,
    0x7AD9, 0x62D4, 0x4AC3, 0x52CE, 0x1AED, 0x02E0, 0x2AF7, 0x32FA,
    0xBAB1, 0xA2BC, 0x8AAB, 0x92A6, 0xDA85, 0xC288, 0xEA9F, 0xF292,
    0xE204, 0xFA09, 0xD21E, 0xCA13, 0x8230, 0x9A3D, 0xB22A, 0xAA27,
    0x226C, 0x3A61, 0x1276, 0x0A7B, 0x4258, 0x5A55, 0x7242, 0x6A4F,
    0xA6DC, 0xBED1, 0x96C6, 0x8ECB, 0xC6E8, 0xDEE5, 0xF6F2, 0xEEFF,
    0x66B4, 0x7EB9, 0x56AE, 0x4EA3, 0x0680, 0x1E8D, 0x369A, 0x2E97,
    0x3E01, 0x260C, 0x0E1B, 0x1616, 0x5E35, 0x4638, 0x6E2F, 0x7622,
    0xFE69, 0xE664, 0xCE73, 0xD67E, 0x9E5D, 0x8650, 0xAE47, 0xB64A,
    0x8F6B, 0x9766, 0xBF71, 0xA77C, 0xEF5F, 0xF752, 0xDF45, 0xC748,
    0x4F03, 0x570E, 0x7F19, 0x6714, 0x2F37, 0x373A, 0x1F2D, 0x0720,
    0x17B6, 0x0FBB, 0x27AC, 0x3FA1, 0x7782, 0x6F8F, 0x4798, 0x5F95,
    0xD7DE, 0xCFD3, 0xE7C4, 0xFFC9, 0xB7EA, 0xAFE7, 0x87F0, 0x9FFD,
    0xF5B2, 0xEDBF, 0xC5A8, 0xDDA5, 0x9586, 0x8D8B, 0xA59C, 0xBD91,
    0x35DA, 0x2DD7, 0x05C0, 0x1DCD, 0x55EE, 0x4DE3, 0x65F4, 0x7DF9,
    0x6D6F, 0x7562, 0x5D75, 0x4578, 0x0D5B, 0x1556, 0x3D41, 0x254C,
    0xAD07, 0xB50A, 0x9D1D, 0x8510, 0xCD33, 0xD53E, 0xFD29, 0xE524,
    0xDC05, 0xC408, 0xEC1F, 0xF412, 0xBC31, 0xA43C, 0x8C2B, 0x9426,
    0x1C6D, 0x0460, 0x2C77, 0x347A, 0x7C59, 0x6454, 0x4C43, 0x544E,
    0x44D8, 0x5CD5, 0x74C2, 0x6CCF, 0x24EC, 0x3CE1, 0x14F6, 0x0CFB,
    0x84B0, 0x9CBD, 0xB4AA, 0xACA7, 0xE484, 0xFC89, 0xD49E, 0xCC93,
}};

static inline uint checksumt(const uchar *data, uint o, uint n, uint g) {
    uint i;
    ushort v;

    for (v = 0xFFFFU, i = o; i < n; i++)
        v = (v << 8 ^ bokcrctable[g][(v >> 8 & 0xFF) ^ data[i]]);
    return (uint)~v & 0xFFFFU;
}

#define BOK1CRC 0x1021
#define BOK2CRC 0x1021
#define BOK3CRC 0x8005
#define BOK4CRC 0x180D
static uint checksum(const uchar *data, uint o, uint n, uint c) {
    uint i, j;
    ushort v;

    for (v = 0xFFFFU, i = o; i < n; i++)
        for (v ^= data[i] << 8, j = 0; j < 8; j++)
            v = v << 1 ^ ((v >> 15 & 1) ? c : 0);
    return (uint)~v & 0xFFFFU;
}

#define KBDINIT(l) (kbdmap[l] != NULL || (kbdmap[l] = mapkbd(l)) != NULL)
#define KBDDIST(l, a, b) (kbdmap[l][(a) * sizeof(kbdtable[l]) + (b)])
static uint *kbdmap[3] = { NULL, NULL, NULL };

static inline int rankb64(uint lang, uchar *s, uint n, uchar oc) {
    uint i;
    int r = 0;
    uchar c;

    /*
     * if (lang >= 2) {
     *     for (i = 0, oc &= 0x3F; i < n; i++, oc = c)
     *         c = s[i] & 0x3F, r += LJ64DIST(oc, c);
     *     return r;
     * }
     */
    if (!KBDINIT(lang))
        return n * n;
    for (i = 0, oc = oc & 0x3F; i < n; i++, oc = c)
        c = s[i] & 0x3F, r += KBDDIST(lang, oc, c);
    return r;
}

static uint deb64(uint lang, uchar *dst, uint dn, const char *src, uint sn) {
    uint i, j, k, l;

    if (lang > 1)
        return 0;
    for (i = 0, j = 0; i < sn; i++) {
        l = U8CLEN(src[i]);
        for (k = 0; k < 64 && strncmp(b64table[lang][k], src + i, l) != 0;
          k++) {}
        if (k < 64) {
            if (j < dn)
                dst[j] = k;
            j++;
        }
    }
    return j;
}

typedef void genname_cb(void *ctx, const uchar *f, uint fn,
  const uchar *n, uint nn, uint o, uint r);

static uint genjpnames(uint g, uint m, genname_cb cb, void *ctx) {
    uint count = 0;
    signed char k[7] = { -1 };
    uchar b[7] = { 0xFF };
    uchar x[7];
    uchar r[7] = { 0 };
    uchar s[5];
    uint i, j;
    uint oi = g == BOK1 ? 14 : 17;
    uint oo = g == BOK1 ? 0 : 2;
    uint oe = g == BOK1 ? 4 : 8;
    uint o, t;

    if (g >= 3)
        return count;
    if (!KBDINIT(JP))
        return count;
    for (i = 0; i < 64; i++) {
        (void)fprintf(stderr, "gennames %u/64\r", i);
        b[0] = i;
        j = 1;
        for (;;) {
            if (j == 0)
                break;
            k[j] = k[j] < 0 ? 0 : k[j] + 1;
            b[j] = k[j];
            if (k[j] >= 0x3F)
                k[j--] = -1;
            else if ((t = KBDDIST(JP, b[j-1], b[j])) + r[j-1] <= m)
                b[j] = k[j], r[j] = r[j-1] + t, j++;
            if (j < sizeof(b))
                continue;
            for (j--, o = 0; o < oe; o++) {
                (void)memcpy(x, b, sizeof(b));
                BOKXOR(x, g, 0, o + oi, sizeof(x));
                getstr(x, s, oo, 5);
                if (!valstr(s, 5, JP))
                    continue;
                cb(ctx, b, sizeof(b), s, sizeof(s), o, r[j]);
                count++;
            }
        }
    }
    (void)fprintf(stderr, "gennames 64/64\n");
    return count;
}

static uint genennames(uint g, uint m, genname_cb *cb, void *ctx) {
    uint count = 0;
    signed char k[13] = { -1 };
    uchar b[13] = { 0xFF };
    uchar x[13];
    uchar r[13] = { 0 };
    uchar s[9];
    uint i, j;
    uint oi = g == BOK1 ? 14 : 17;
    uint oo = g == BOK1 ? 1 : 3;
    uint o, t;

    if (g >= 3)
        return count;
    if (!KBDINIT(EN))
        return count;
    for (i = 0; i < 64; i++) {
        (void)fprintf(stderr, "gennames %u/64\r", i);
        b[0] = i;
        j = 1;
        for (;;) {
            if (j == 0)
                break;
            k[j] = k[j] < 0 ? 0 : k[j] + 1;
            b[j] = k[j];
            if (k[j] >= 0x3F)
                k[j--] = -1;
            else if ((t = KBDDIST(EN, b[j-1], b[j])) + r[j-1] <= m)
                r[j] = r[j-1] + t, j++;
            if (j < sizeof(b))
                continue;
            for (j--, o = 0; o < 4; o++) {
                (void)memcpy(x, b, sizeof(b));
                BOKXOR(x, g, 0, o + oi, sizeof(b));
                getstr(x, s, oo, 9);
                if (!valstr(s, 9, EN))
                    continue;
                cb(ctx, b, sizeof(b), s, sizeof(s), o, r[j]);
                count++;
            }
        }
    }
    (void)fprintf(stderr, "gennames 64/64\n");
    return count;
}

static uint genljnames(uint g, uint m, genname_cb cb, void *ctx) {
    uint count = 0;
    signed char k[7] = { -1 };
    uchar b[7] = { 0xFF };
    uchar x[7];
    uchar r[7] = { 0 };
    uchar s[5];
    uint i, j;
    uint oi = 17;
    uint oo = 2;
    uint oe = g == BOK1 ? 4 : 8;
    uint o, t;

    if (g < BOK3)
        return count;
    if (!KBDINIT(LKJ))
        return count;
    for (i = 0; i < 64; i++) {
        (void)fprintf(stderr, "gennames %u/64\r", i);
        b[0] = i;
        j = 1;
        for (;;) {
            if (j == 0)
                break;
            k[j] = k[j] < 0 ? 0 : k[j] + 1;
            if (k[j] >= 0x3F)
                k[j--] = -1;
            else if ((t = KBDDIST(LKJ, b[j-1], k[j])) + r[j-1] <= m)
                b[j] = k[j], r[j] = r[j-1] + t, j++;
            if (j < sizeof(b))
                continue;
            for (j--, o = 0; o < oe; o++) {
                (void)memcpy(x, b, sizeof(b));
                BOKXOR(x, g, 0, o + oi, sizeof(x));
                getstr(x, s, oo, 5);
                if (!valstr(s, 5, JP))
                    continue;
                cb(ctx, b, sizeof(b), s, sizeof(s), o, r[j]);
                count++;
            }
        }
    }
    (void)fprintf(stderr, "gennames 64/64\n");
    return count;
}

struct password {
    struct password *next;
    uchar password[32];
    uchar name[9];
    uchar plength;
    uchar nlength;
    uchar lang;
    uchar region;
    uchar offset;
    uchar sub;
    uchar rank;
};

#define PASSHEAD_INITIALIZER { NULL, NULL }
struct passhead {
    struct password *head;
    struct password *tail;
};

static void appendpassword(struct password **freelist, struct passhead *head,
  const uchar *pass, uint plen, const uchar *name, uint nlen, uint lang,
  uint region, uint offset, uint sub, uint rank) {
    struct password *p;

    if (freelist != NULL && *freelist != NULL) {
        p = *freelist;
        *freelist = p->next;
    } else if ((p = malloc(sizeof(*p))) == NULL)
        return;
    p->plength = MIN(sizeof(p->password), plen);
    (void)memcpy(p->password, pass, p->plength);
    p->nlength = MIN(sizeof(p->name), nlen);
    (void)memcpy(p->name, name, p->nlength);
    p->lang = lang;
    p->region = region;
    p->offset = offset;
    p->sub = sub;
    p->rank = rank;
    p->next = NULL;
    if (head->tail != NULL)
        head->tail->next = p;
    if (head->head == NULL)
        head->head = p;
    head->tail = p;
}

static void freepasswords(struct password *p) {
    struct password *n;

    for (; p != NULL; p = n) {
        n = p->next;
        free(p);
    }
}

static void clearpasswords(struct password **freelist, struct passhead *head) {

    if (freelist != NULL) {
        if (head->tail != NULL)
            head->tail->next = *freelist;
        *freelist = head->head;
        head->head = head->tail = NULL;
    } else {
        freepasswords(head->head);
        head->head = head->tail = NULL;
    }
}

static void uniqpasswords(struct password **freelist, struct passhead *head) {
    struct password *c, *p, *q;

    for (c = head->head; c != NULL; c = c->next)
        for (p = c, q = c->next; q != NULL;
          p = q, q = q != NULL ? q->next : NULL)
            while (q != NULL &&
              memcmp(c->password, q->password, c->plength) == 0) {
                p->next = q->next;
                if (q == head->tail)
                    head->tail = p;
                if (freelist != NULL)
                    q->next = *freelist, *freelist = q;
                else
                    free(q);
                q = p->next;
            }
}

#define GENNAME_CTX_INIT(p, r) \
do { \
    uint _i; \
    for (_i = 0; _i < nitems((p)->heads); _i++) { \
        (p)->heads[_i].head = NULL; \
        (p)->heads[_i].tail = NULL; \
        (p)->ranks[_i] = (r); \
    } \
    (p)->free = NULL; \
} while (0)

#define GENNAME_CTX_CLEAR(p, r) \
do { \
    uint _i; \
    for (_i = 0; _i < nitems((p)->heads); _i++) { \
        clearpasswords(&(p)->free, &(p)->heads[_i]); \
        (p)->ranks[_i] = (r); \
    } \
} while (0)

#define GENNAME_CTX_PRINT(p, g) \
do { \
    struct password *_p; \
    uint _i; \
    for (_i = 0; _i < nitems((p)->heads); _i++) { \
        uniqpasswords(&(p)->free, (p)->heads + _i); \
        for (_p = (p)->heads[_i].head; _p != NULL; _p = _p->next) \
            (void)printpassword(_p, (g)); \
    } \
} while (0)

struct genname_ctx {
    struct passhead heads[64];
    int ranks[64];
    struct password *free;
};

static void bok1jpcb(void *ctx, const uchar *f, uint fn, const uchar *n,
  uint nn, uint o, uint nr) {
    uchar b[32];
    struct genname_ctx *dst = ctx;
    uint r, i, j, k;

    fn = fn > 7 ? 7 : fn;
    nn = nn > 5 ? 5 : nn;
    for (i = 0; i < 64; i++) {
        (void)memset(b, i, 32);
        (void)memcpy(b + 14, f, fn);
        b[23] = b[22] = b[21] = b[20];
        BOKXOR(b, BOK1, 4, o, 24);
        if ((k = getint(b, BOK1MINUTES(JP))) >= 60)
            continue;
        if (k == 0 && getint(b, BOK1HOURS(JP)) == 0)
            continue;
        if (getint(b, BOK1CLEARS(JP)) == 0)
            continue;
        putint(b, 1, BOK1REGION(JP));
        putint(b, o, BOK1OFFSET(JP));
        for (j = 0; j < 16; j++) {
            putint(b, j, BOK1TITLE(JP));
            BOKXOR(b, BOK1, 4, o, 24);
            putint(b, checksumt(b, 4, 24, BOK1), BOK1CHECKSUM(JP));
            for (k = 0; k < 8; k++) {
                putint(b, k, 21, 3, 0);
                r = rankb64(JP, b, 24, 0);
                if (dst->ranks[j] < 0 || dst->ranks[j] > r) {
                    clearpasswords(&dst->free, &dst->heads[j]);
                    dst->ranks[j] = r;
                }
                if (dst->ranks[j] < r)
                    continue;
                appendpassword(&dst->free, &dst->heads[j], b, 24, n, nn, JP, 1,
                  o, j, r);
            }
            BOKXOR(b, BOK1, 4, o, 24);
        }
    }
}

static void bok1encb(void *ctx, const uchar *f, uint fn, const uchar *n,
  uint nn, uint o, uint nr) {
    uchar b[32];
    struct genname_ctx *dst = ctx;
    uint r, i, j, k;

    fn = fn > 13 ? 13 : fn;
    nn = nn > 9 ? 9 : nn;
    for (i = 0; i < 64; i++) {
        (void)memset(b, i, 32);
        (void)memcpy(b + 14, f, fn);
        b[31] = b[30] = b[29] = b[28] = b[27] = b[26];
        BOKXOR(b, BOK1, 4, o, 32);
        if ((k = getint(b, BOK1MINUTES(EN))) >= 60)
            continue;
        if (k == 0 && getint(b, BOK1HOURS(EN)) == 0)
            continue;
        if (getint(b, BOK1CLEARS(EN)) == 0)
            continue;
        putint(b, o, BOK1OFFSET(EN));
        for (j = 0; j < 32; j++) {
            putint(b, j & 15, BOK1TITLE(EN));
            BOKXOR(b, BOK1, 4, o, 32);
            putint(b, checksumt(b, 4, 32, BOK1), BOK1CHECKSUM(EN));
            for (k = 0; k < 8; k++) {
                putint(b, 2 | (j >> 4 & 1), BOK1REGION(EN));
                putint(b, k, 21, 3, 0);
                r = rankb64(EN, b, 32, 0);
                if (dst->ranks[j] < 0 || dst->ranks[j] > r) {
                    clearpasswords(&dst->free, &dst->heads[j]);
                    dst->ranks[j] = r;
                }
                if (dst->ranks[j] < r)
                    continue;
                appendpassword(&dst->free, &dst->heads[j], b, 32, n, nn, EN,
                    2 | (j >> 4 & 1), o, j & 15, r);
            }
            BOKXOR(b, BOK1, 4, o, 32);
        }
    }
}

static void bok2jpcb(void *ctx, const uchar *f, uint fn, const uchar *n,
  uint nn, uint o, uint nr) {
    uchar b[32];
    struct genname_ctx *dst = ctx;
    uint r, i, j, k;

    fn = fn > 7 ? 7 : fn;
    nn = nn > 5 ? 5 : nn;
    for (i = 0; i < 64; i++) {
        (void)memset(b, i, 24);
        (void)memcpy(b + 17, f, fn);
        BOKXOR(b, BOK2, 4, o, 24);
        putint(b, 1, BOK2REGION(JP));
        putint(b, o, BOK2OFFSET(JP));
        for (j = 0; j < 32; j++) {
            putint(b, j << 7 | 0x7F, BOK2TITLES(JP));
            BOKXOR(b, BOK2, 4, o, 24);
            putint(b, checksumt(b, 3, 24, BOK2), BOK2CHECKSUM(JP));
            for (k = 0; k < 4; k++) {
                putint(b, k, BOK2HEADERPADDING(JP));
                r = rankb64(JP, b, 24, 0);
                if (dst->ranks[j] < 0 || dst->ranks[j] > r) {
                    clearpasswords(&dst->free, &dst->heads[j]);
                    dst->ranks[j] = r;
                }
                if (dst->ranks[j] < r)
                    continue;
                appendpassword(&dst->free, &dst->heads[j], b, 24, n, nn, JP, 1,
                  o, j, r);
            }
            BOKXOR(b, BOK2, 4, o, 24);
        }
    }
}

static void bok3jpcb(void *ctx, const uchar *f, uint fn, const uchar *n,
  uint nn, uint o, uint nr) {
    uchar b[32];
    struct genname_ctx *dst = ctx;
    uint r, i, j, k;
    int v;

    fn = fn > 7 ? 7 : fn;
    nn = nn > 5 ? 5 : nn;
    for (i = 0; i < 64; i++) {
        (void)memset(b, i, 24);
        (void)memcpy(b + 17, f, fn);
        BOKXOR(b, BOK3, 4, o, 24);
        putint(b, 1, BOK3REGION(JP));
        putint(b, o, BOK3OFFSET(JP));
        if ((v = getint(b, BOK3TIMEZONE(JP))) == 0 || v > 53)
            continue;
        for (j = 0x115; j < 0x400; j = j + 1 | 0x115) {
            if (j & 0x200) {
                j = 0x3FF;
                putint(b, 0, BOK3TITLEPADDING(JP));
            }
            putint(b, j, BOK3TITLES(JP));
            BOKXOR(b, BOK3, 4, o, 24);
            putint(b, checksumt(b, 3, 24, BOK3), BOK3CHECKSUM(JP));
            v = ((j & 0x200) >> 4) | ((j & 0xE0) >> 3) |
              ((j & 8) >> 2) | ((j & 2) >> 1);
            for (k = 0; k < 4; k++) {
                putint(b, k, BOK3HEADERPADDING(JP));
                r = rankb64(LKJ, b, 24, 0);
                if (dst->ranks[v] < 0 || dst->ranks[v] > r) {
                    clearpasswords(&dst->free, &dst->heads[v]);
                    dst->ranks[v] = r;
                }
                if (dst->ranks[v] < r)
                    continue;
                appendpassword(&dst->free, &dst->heads[v], b, 24, n, nn, JP, 1,
                  o, v, r);
            }
            BOKXOR(b, BOK3, 4, o, 24);
        }
    }
}

static int fprintb64(FILE *fp, uint lang, const uchar *str, uint n, uint sp) {
    uint i, w;

    if (lang > 1)
        return -1;
    w = n + (lang > 0 ? n : 0) + (sp > 0 ? (n+1) / sp - 1 : 0);
    for (i = 0; i < n; i++)
        if ((sp > 0 && i > 0 && i % sp == 0 && fputc(' ', fp) < 0) ||
          fputs(b64table[lang][str[i]&0x3F], fp) < 0)
            return -1;
    return w;
}

static int printb64(uint lang, const uchar *str, uint n, uint sp) {

    return fprintb64(stdout, lang, str, n, sp);
}

static int fprintstr(FILE *fp, uint lang, const uchar *str, uint n, uint a,
  char q) {
    uint i, w;

    for (i = 0, w = 0; i < n && str[i] != 0; i++)
        w += lang == JP ? jwidthtable[str[i]] : 1;
    if (q != 0)
        w += 2;
    for (i = w; i < a; i++)
        if (fputc(' ', fp) < 0)
            return -1;
    if (q != 0 && fputc(q, fp) < 0)
        return -1;
    for (i = 0; i < n; i++)
        if (fputs(utftable[lang&1][str[i]], fp) < 0)
            return -1;
    if (q != 0 && fputc(q, fp) < 0)
        return -1;
    return MAX(w, a);
}

static int printstr(uint lang, const uchar *str, uint n, uint a, char q) {

    return fprintstr(stdout, lang, str, n, a, q);
}

static int fprintsub(FILE *fp, uint game, uint sub) {
    uint i, w = 0;

    switch (game) {
    case BOK1:
        return fprintf(fp, "%2u", sub);
    case BOK2:
        w = 5;
        break;
    case BOK3:
        w = 6;
        break;
    }
    if (w == 0)
        return 0;
    if (fputc('0', fp) < 0 || fputc('x', fp) < 0)
        return -1;
    for (i = 0; i < w; i++)
        if (fputc((sub >> (w - i - 1) & 1) ? '1' : '0', fp) < 0)
            return -1;
    return w + 2;
}

static int printsub(uint game, uint sub) {

    return fprintsub(stdout, game, sub);
}

const char * const regioncode[4] = { "??", "jp", "na", "eu" };
const uchar regionlang[4] = { JP, JP, EN, EN };

static int fprintpassword(FILE *fp, struct password *p, uint g) {
    int t, r = 0;

    if ((t = fprintf(fp, "%2s %2u ", regioncode[p->region < nitems(regioncode) ?
      p->region : 0], p->rank)) < 0)
        return -1;
    r += t;
    if ((t = fprintsub(fp, g, p->sub)) < 0)
        return -1;
    r += t;
    if (fputc(' ', fp) < 0)
        return -1;
    r++;
    if ((t = fprintstr(fp, regionlang[p->region], p->name, p->nlength,
      p->nlength == 5 ? 12 : 11, '"')) < 0)
        return -1;
    r += t;
    if (fputc(' ', fp) < 0)
        return -1;
    r++;
    if ((t = fprintb64(fp, regionlang[p->region], p->password, p->plength,
      p->plength % 6 == 0 ? 6 : 8)) < 0)
        return -1;
    r += t;
    if (fputc('\n', fp) < 0)
        return -1;
    r++;
    return r;
}

static int printpassword(struct password *p, uint g) {

    return fprintpassword(stdout, p, g);
}

static int fprintpassstr(FILE *fp, const char *str, uint g, int opt) {
    struct password p;
    uchar j[24];
    uchar e[32];
    uint h, b, f, hs, hn, bs, bn, fs, fn, t;
    uint en = 0;
    uint jn = 0;
    uint sn;
    int lang = -1;

    if (g > BOK3)
        return 0;
    sn = strlen(str);
    jn = deb64(JP, j, sizeof(j), str, sn);
    en = deb64(EN, e, sizeof(e), str, sn);
    if ((jn == sizeof(j) && en == sizeof(e)) ||
      (jn != sizeof(j) && en != sizeof(e)))
        return -1;
    if (jn == sizeof(j)) {
        lang = g < BOK3 ? JP : LKJ;
        (void)memcpy(p.password, j, (p.plength = sizeof(j)));
    } else if (g < BOK3) {
        lang = EN;
        (void)memcpy(p.password, e, (p.plength = sizeof(e)));
    } else
        return -1;
    hs = g == BOK1 ? 21 : 16;
    hn = g == BOK1 ? 3 : 2;
    bs = g == BOK1 ? 84 : 102;
    bn = ((lang&1) == JP ? 0 : 1) + (g == BOK1 ? 0 : 2);
    fs = ((lang&1) == JP ? 0 : 33) + (g == BOK1 ? 124 : 144);
    fn = ((lang&1) == JP ? 0 : 3) + (g == BOK1 ? 2 : 0);
    if (opt < 1)
        hn = bn = fn = 0;
    if (opt < 2 && g != BOK2) /* to avoid clobbering titles */
        bn = 0;
    p.lang = lang;
    p.nlength = (lang&1) == JP ? 5 : 9;
    p.rank = rankb64(lang, p.password, p.plength, 0);
    if (opt > 0) {
        (void)memcpy(e, p.password, p.plength);
        for (h = 0; h < (1U << hn); h++) {
            putint(e, h, hs, hn, 0);
            for (b = 0; b < (1U << bn); b++) {
                putint(e, b, bs, bn, 0);
                for (f = 0; f < (1U << fn); f++) {
                    putint(e, f, fs, fn, 0);
                    t = checksumt(e, g == BOK1 ? 4 : 3, p.plength, g);
                    if (g == BOK1)
                        putint(e, t, BOK1CHECKSUM(lang));
                    else
                        putint(e, t, 0, 16, 0);
                    if ((t = rankb64(lang, e, p.plength, 0)) >= p.rank)
                        continue;
                    (void)memcpy(p.password, e, p.plength);
                    p.rank = t;
                }
            }
        }
    }
    switch (g) {
    case BOK1:
        p.region = getint(p.password, BOK1REGION(lang));
        p.offset = getint(p.password, BOK1OFFSET(lang));
        (void)memcpy(e, p.password, p.plength);
        BOKXOR(e, g, 4, p.offset, p.plength);
        p.sub = getint(e, BOK1TITLE(lang));
        getstr(e, p.name, BOK1NAME(lang));
        break;
    case BOK2:
        p.region = getint(p.password, BOK2REGION(lang));
        p.offset = getint(p.password, BOK2OFFSET(lang));
        BOKXOR(e, g, 4, p.offset, p.plength);
        p.sub = getint(e, BOK2TITLES(lang)) >> 7;
        getstr(e, p.name, BOK2NAME(lang));
        break;
    case BOK3:
        p.region = getint(p.password, BOK3REGION(lang));
        p.offset = getint(p.password, BOK3OFFSET(lang));
        (void)memcpy(e, p.password, p.plength);
        BOKXOR(e, g, 4, p.offset, p.plength);
        p.sub = getint(e, BOK3ENDINGS(lang));
        getstr(e, p.name, BOK3NAME(lang));
        break;
    }
    p.region = p.region < 4 ? p.region : lang == EN ? 2 : 1;
    return fprintpassword(fp, &p, g);
}

static int printpassstr(const char *str, uint g, int opt) {

    return fprintpassstr(stdout, str, g, opt);
}

static void ranknames(struct genname_ctx *ctx, genname_cb *cb,
  const char *names, uint game, uint lang) {
    uchar p[13] = { 0 };
    uchar x[13];
    uchar n[9];
    uint oi = game == BOK1 ? 14 : 17;
    uint oo = ((lang&1) == JP ? 0 : 1) + (game == BOK1 ? 0 : 2);
    uint oe = game == BOK1 ? 4 : 8;
    uint pm = (lang&1) == JP ? 7 : 13;
    uint nm = (lang&1) == JP ? 5 : 9;
    uint of = ((lang&1) == JP ? 0 : 3) + (game == BOK1 ? 2 : 0);
    uint i, l, o, b, e;

    for (i = 0; names[i] != '\0'; i += l, i += names[i] == ':') {
        for (l = 0; names[i+l] != '\0' && names[i+l] != ':'; l++) {}
        (void)encstr(n, nm, names + i, l, lang&1);
        if (!valstr(n, nm, lang&1))
            continue;
        putstr(p, n, oo, nm);
        for (b = 0; b < (1U << oo); b++) {
            putint(p, b, 0, oo, 0);
            for (e = 0; e < (1U << of); e++) {
                putint(p, e, pm * 6 - of, of, 0);
                for (o = 0; o < oe; o++) {
                    (void)memcpy(x, p, pm);
                    BOKXOR(x, game, 0, o + oi, pm);
                    cb(ctx, x, pm, n, nm, o, 0);
                }
            }
        }
    }
}

int main(int argc, char *argv[]) {
    struct genname_ctx ctx;
    genname_cb *jcb = NULL;
    genname_cb *ecb = NULL;
    genname_cb *lcb = NULL;
    const char *ofile = "-";
    const char *arg;
    const char *names = NULL;
    FILE *fp;
    int i, j, k;
    int game = BOK1;
    uint jmax = (uint)~0U;
    uint emax = (uint)~0U;
    int opt = 0;

    GENNAME_CTX_INIT(&ctx, -1);
    for (i = 1; i < argc; i++) {
        if (argv[i][0] != '-' || argv[i][1] == '\0')
            break;
        if (argv[i][1] == '-' && argv[i][2] == '\0') {
            i++;
            break;
        }
        for (j = 1; argv[i][j] != '\0'; j++)
            switch (argv[i][j]) {
            case '1':
                game = BOK1;
                break;
            case '2':
                game = BOK2;
                break;
            case '3':
                game = BOK3;
                break;
            case 'O':
                opt++;
                break;
            case 'e':
                if ((arg = argv[i][j+1] != '\0' ? argv[i] + ++j :
                  i + 1 < argc ? (j = 0, argv[++i]) : NULL) == NULL)
                    goto usage;
                if (sscanf(arg, "%u%n", &emax, &k) != 1 || k == 0 ||
                  arg[k] != '\0')
                    goto usage;
                if (emax > 15) {
                    (void)fprintf(stderr, "too big: %s", arg);
                    goto usage;
                }
                j += k;
                break;
            case 'j':
                if ((arg = argv[i][j+1] != '\0' ? argv[i] + ++j :
                  i + 1 < argc ? (j = 0, argv[++i]) : NULL) == NULL)
                    goto usage;
                if (sscanf(arg, "%u%n", &jmax, &k) != 1 || k == 0 ||
                  arg[k] != '\0')
                    goto usage;
                if (jmax > 10) {
                    (void)fprintf(stderr, "too big: %s", arg);
                    goto usage;
                }
                j += k;
                break;
            case 'n':
                if ((arg = argv[i][j+1] != '\0' ? argv[i] + ++j :
                  i + 1 < argc ? (j = 0, argv[++i]) : NULL) == NULL)
                    goto usage;
                names = arg;
                j += strlen(arg) - 1;
                break;
            case 'o':
                if ((arg = argv[i][j+1] != '\0' ? argv[i] + ++j :
                  i + 1 < argc ? (j = 0, argv[++i]) : NULL) == NULL)
                    goto usage;
                ofile = arg;
                break;
            default:
                (void)fprintf(stderr, "bokgen: unknown option -- %c\n",
                  argv[i][j]);
            usage:
                (void)fprintf(stderr, "usage: bokgen [-123O] "
                  "[-e max-moves] [-j max-moves] [-n name[:name...]]\n"
                  "              [-o output-file] [password ...]\n");
                return 1;
            }
    }
    argc -= i;
    argv += i;
    for (i = 0; i < argc; i++)
        if (printpassstr(argv[i], game, opt) < 0)
            (void)fprintf(stderr, "invalid password: %s\n", argv[i]);
    if (ofile == NULL || (ofile[0] == '-' && ofile[1] == '\0'))
        fp = stdout;
    else if ((fp = fopen(ofile, "w")) == NULL) {
        (void)fprintf(stderr, "could not open: %s\n", ofile);
        goto usage;
    }
    jcb = game == BOK1 ? &bok1jpcb : game == BOK2 ? &bok2jpcb : NULL;
    ecb = game == BOK1 ? &bok1encb : NULL;
    lcb = game == BOK3 ? &bok3jpcb : NULL;
    if (names != NULL && jcb != NULL) {
        GENNAME_CTX_CLEAR(&ctx, 99);
        ranknames(&ctx, jcb, names, game, JP);
        GENNAME_CTX_PRINT(&ctx, game);
    }
    if (names != NULL && ecb != NULL) {
        GENNAME_CTX_CLEAR(&ctx, 99);
        ranknames(&ctx, ecb, names, game, EN);
        GENNAME_CTX_PRINT(&ctx, game);
    }
    if (names != NULL && lcb != NULL) {
        GENNAME_CTX_CLEAR(&ctx, 99);
        ranknames(&ctx, lcb, names, game, LKJ);
        GENNAME_CTX_PRINT(&ctx, game);
    }
    jcb = ~jmax != 0 ? jcb : NULL;
    ecb = ~emax != 0 ? ecb : NULL;
    lcb = ~jmax != 0 ? lcb : NULL;
    if (jcb != NULL) {
        GENNAME_CTX_CLEAR(&ctx, 99);
        (void)genjpnames(game, jmax, jcb, &ctx);
        GENNAME_CTX_PRINT(&ctx, game);
    }
    if (ecb != NULL) {
        GENNAME_CTX_CLEAR(&ctx, 99);
        (void)genennames(game, emax, ecb, &ctx);
        GENNAME_CTX_PRINT(&ctx, game);
    }
    if (lcb != NULL) {
        GENNAME_CTX_CLEAR(&ctx, 99);
        (void)genljnames(game, jmax, lcb, &ctx);
        GENNAME_CTX_PRINT(&ctx, game);
    }
    return 0;
}
